﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Wicresoft.EKA.CommonExtensions;

namespace Wicresoft.EKA.NRemedy
{
    public class ARLoginContextAdminManyPool : ICleanUp
    {
        private static Dictionary<string, ARLoginContextAdminManyPool> _instances = new Dictionary<string, ARLoginContextAdminManyPool>();

        private static readonly Object lockObject = new Object();

        private Dictionary<string, ARLoginContextAdminAndLastUsedTime> items;

        //private Dictionary<string,LogInInfo>

        private ARLoginContextAdminManyPool()
        {
            items = new Dictionary<string, ARLoginContextAdminAndLastUsedTime>();
            CleanUpThread cleanThread = new CleanUpThread(this, this._sleepTime);
            Thread t = new Thread(new ThreadStart(cleanThread.run));
            t.Start();
        }

        //public class LogInInfo
        //{
        public string Server { get; set; }
        public string AdminUser { get; set; }
        public string AdminPassword { get; set; }
        public string AdminAuthentication { get; set; }
        //}

        public static void Add(string key, string server, string user, string password, string auth)
        {
            lock (lockObject)
            {
                //直接退出，可以防止由于重复添加导致无法释放资源的可能性。
                if (_instances.ContainsKey(key)) return;
                _instances.Add(key, new ARLoginContextAdminManyPool()
                {
                    Server = server,
                    AdminUser = user,
                    AdminPassword = password,
                    AdminAuthentication = auth,
                });
            }
        }

        public static ARLoginContextAdminManyPool GetInstance(string key)
        {
            if (_instances.ContainsKey(key)) return _instances[key];
            throw new InvalidOperationException("ARLoginContextAdminManyPool don't contain the key: " + key);
        }

        public IARServer CreateARServer()
        {
            return new ARServer();
        }

        public void Return(string key)
        {
            //lock (items)
            //{
            items[key].IsInUse = false;
            items[key].LastUsedTime = DateTime.Now;
            Monitor.Pulse(items);
            //}
        }


        public ARLoginContext GetLoginContext(out string key)
        {
            var resultNo = items.Where(o => o.Value.IsInUse == false);

            if (resultNo.Count() > 0)
            {
                var result = resultNo.Where(o => o.Value.IsInUse == false).FirstOrDefault();
                result.Value.IsInUse = true;
                result.Value.LastUsedTime = DateTime.Now;
                key = result.Key;
                return result.Value.LogInContext;
            }

            lock (items)
            {
                if (items.Count(o => o.Value.IsInUse == false) > 0)
                {
                    var result = items.Where(o => o.Value.IsInUse == false).FirstOrDefault();
                    result.Value.IsInUse = true;
                    result.Value.LastUsedTime = DateTime.Now;
                    key = result.Key;
                    return result.Value.LogInContext;
                }

                int count = items.Keys.Count;
                if (count < this._poolSize || 0 == _poolSize)
                {
                    string keyGe = Guid.NewGuid().ToString();
                    var loginContext = new ARLoginContext(Server, AdminUser, AdminPassword, new ARServerFactoryDefault(), AdminAuthentication);
                    items.Add(keyGe, new ARLoginContextAdminAndLastUsedTime()
                    {
                        LogInContext = loginContext,
                        LastUsedTime = DateTime.Now,
                        IsInUse = true
                    });

                    key = keyGe;
                    return loginContext;
                }
                else
                {
                    while (true)
                    {
                        Monitor.Wait(items);

                        if (items.Count(o => o.Value.IsInUse == false) > 0)
                        {
                            var result = items.Where(o => o.Value.IsInUse == false).FirstOrDefault();
                            result.Value.IsInUse = true;
                            result.Value.LastUsedTime = DateTime.Now;
                            key = result.Key;
                            return result.Value.LogInContext;
                        }

                        int count1 = items.Keys.Count;
                        if (count1 < this._poolSize || 0 == _poolSize)
                        {
                            string keyGe = Guid.NewGuid().ToString();
                            var loginContext = new ARLoginContext(Server, AdminUser, AdminPassword, new ARServerFactoryDefault(), AdminAuthentication);
                            items.Add(keyGe, new ARLoginContextAdminAndLastUsedTime()
                            {
                                LogInContext = loginContext,
                                LastUsedTime = DateTime.Now,
                                IsInUse = true
                            });

                            key = keyGe;
                            return loginContext;
                        }

                    }
                }

            }
        }

        private int _expirationTime = 2 * 60 * 60 * 1000;

        public int ExpirationTime
        {
            get
            {
                return this._expirationTime;
            }
            set
            {
                this._expirationTime = value;
            }
        }

        private int _sleepTime = 5 * 60 * 1000;

        public int SleepTime
        {
            get
            {
                return this._sleepTime;
            }
            set
            {
                this._sleepTime = value;
            }
        }

        private int _poolSize = 0;

        public int PoolSize
        {
            get
            {
                return this._poolSize;
            }
            set
            {
                this._poolSize = value;
            }
        }

        public void Clean()
        {
            lock (items)
            {
                var expires = items.Where(i => (i.Value.LastUsedTime.AddMilliseconds(_expirationTime)) <= (DateTime.Now)).ToList();
                foreach (var item in expires)
                {
                    ARLoginContextAdminAndLastUsedTime asa = item.Value;
                    if (!asa.IsInUse && asa.LastUsedTime.AddMilliseconds(_expirationTime) <= DateTime.Now)
                    {
                        items.Remove(item.Key);
                        asa.Dispose();
                    }
                    Monitor.Pulse(items);
                }
            }
        }


    }
}
